package edu.upol.paradise.utils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;

import edu.upol.paradise.compiler.CompilerError;
import edu.upol.paradise.compiler.types.AnnotatedCons;
import edu.upol.paradise.compiler.types.Cons;
import edu.upol.paradise.compiler.types.Nil;
import edu.upol.paradise.compiler.types.ParadiseObject;
import edu.upol.paradise.compiler.types.ParadisePair;
import edu.upol.paradise.compiler.types.ParadiseSymbol;

public class Utils {
	static final Logger log = Logger.getLogger(Utils.class);

	public static List<ElementMetadata> annotatedPair2List(ParadisePair p) {
		List<ElementMetadata> list = new ArrayList<ElementMetadata>();
		while (p != Nil.NIL){
			ElementMetadata em = new ElementMetadata();
			em.element = p.car();
			if (p instanceof AnnotatedCons){
				AnnotatedCons ac = (AnnotatedCons)p;
				em.charNo = ac.getCharNo();
				em.lineNo = ac.getLineNo();
			} else {
				log.debug("Not an annotated cons!");
			}
			list.add(em);
			if (!(p.cdr() instanceof ParadisePair)){
				em = new ElementMetadata();
				em.element = p.cdr();
				if (p instanceof AnnotatedCons){
					AnnotatedCons ac = (AnnotatedCons)p;
					em.charNo = ac.getCharNo();
					em.lineNo = ac.getLineNo();
				} else {
					log.debug("Not an annotated cons!");
				}
				list.add(em);
				break;
			}
			p = (ParadisePair) p.cdr();
		}
		return list;
	}

	private static long cmpCounter = Long.MIN_VALUE;
	public static String genName() {
		return "__internal_paradise_block<" + Long.toHexString(cmpCounter++) + ">";
	}
	
	public static ParadiseObject array2list(ParadiseObject[] objects) {
		ParadisePair head = new Cons();
		Cons current = (Cons) head;
		
		for (int i=0; i<objects.length; i++){
			ParadiseObject o = objects[i];
			current.setCar(o);
			if (i != objects.length-1){
				current.setCdr(new Cons());
				current = (Cons) current.cdr();
			}
		}
		
		return head;
	}
	
	public static ParadiseObject asPair(List<ParadiseObject> astTree) {
		return asPair(astTree, false);
	}

	public static ParadiseObject asPair(List<ParadiseObject> astTree, boolean annotated) {
		Cons head = null;
		Cons top = null;
		for (ParadiseObject p : astTree){
			if (top == null){
				head = top = annotated ? new AnnotatedCons() : new Cons();
				top.setCar(p);
				continue;
			}
			Cons el = annotated ? new AnnotatedCons() : new Cons();
			el.setCar(p);
			top.setCdr(el);
			top = el;
		}
		return head;
	}

	public static List<ParadiseSymbol> pair2alist(ParadiseObject plist) throws CompilerError {
		try {
			List<ParadiseSymbol> list = new ArrayList<ParadiseSymbol>();
			while (!plist.isAtom() && plist != Nil.NIL){
				ParadiseObject oo = ((ParadisePair)plist).car();
				if (oo instanceof ParadiseSymbol)
					list.add((ParadiseSymbol)oo);
				plist = ((ParadisePair)plist).cdr();
			}
			if (plist.isAtom() && plist instanceof ParadisePair)
				list.add((ParadiseSymbol)plist);
			
			return list;
		} catch (Exception e){
			throw new CompilerError("Illegal component, exception: " + e.getMessage());
		}
	}

	public static HashSet<String> setBoth(Set<String> sset, Set<String> set) {
		HashSet<String> result = new HashSet<String>();
		Set<String> smaller = sset.size() >= set.size() ? set : sset;
		Set<String> larger = sset.size() < set.size() ? sset : set;
		for (String s : smaller)
			if (larger.contains(s))
				result.add(s);
		return result;
	}
	
}
